#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
MCP 计算器服务高级工具示例

本文件展示了计算器服务的高级数学运算工具实现，包括：
- 三角函数
- 对数函数
- 异步计算
- 缓存机制
- 统计函数
- 复杂方程求解
"""

from mcp.server.fastmcp import FastMCP
from typing import List, Union, Optional, Dict
import math
import asyncio
import time
from functools import wraps
import hashlib

# 创建 MCP 服务实例
mcp = FastMCP("Advanced Calculator Tools")

# ============================================================================
# 缓存装饰器
# ============================================================================

def timed_cache(seconds: int):
    """带过期时间的缓存装饰器

    Args:
        seconds: 缓存过期时间（秒）
    """
    def decorator(func):
        cache = {}
        
        @wraps(func)
        def wrapper(*args, **kwargs):
            # 创建缓存键
            key = str(args) + str(sorted(kwargs.items()))
            key_hash = hashlib.md5(key.encode()).hexdigest()
            
            # 检查缓存
            now = time.time()
            if key_hash in cache:
                result, timestamp = cache[key_hash]
                if now - timestamp < seconds:
                    return result
                # 缓存过期，删除
                del cache[key_hash]
            
            # 执行函数并缓存结果
            result = func(*args, **kwargs)
            cache[key_hash] = (result, now)
            return result
        return wrapper
    return decorator

# ============================================================================
# 三角函数工具
# ============================================================================

@mcp.tool()
def sin(x: float, unit: str = "radian") -> float:
    """Calculate sine of x

    Args:
        x: Input value
        unit: Unit of x (radian or degree)

    Returns:
        Sine of x

    Raises:
        ValueError: If unit is not 'radian' or 'degree'
    """
    if unit == "degree":
        x = math.radians(x)
    elif unit != "radian":
        raise ValueError(f"Invalid unit: {unit}. Use 'radian' or 'degree'")
    return math.sin(x)

@mcp.tool()
def cos(x: float, unit: str = "radian") -> float:
    """Calculate cosine of x

    Args:
        x: Input value
        unit: Unit of x (radian or degree)

    Returns:
        Cosine of x
    """
    if unit == "degree":
        x = math.radians(x)
    elif unit != "radian":
        raise ValueError(f"Invalid unit: {unit}. Use 'radian' or 'degree'")
    return math.cos(x)

@mcp.tool()
def tan(x: float, unit: str = "radian") -> float:
    """Calculate tangent of x

    Args:
        x: Input value
        unit: Unit of x (radian or degree)

    Returns:
        Tangent of x
    """
    if unit == "degree":
        x = math.radians(x)
    elif unit != "radian":
        raise ValueError(f"Invalid unit: {unit}. Use 'radian' or 'degree'")
    return math.tan(x)

# ============================================================================
# 对数和指数函数
# ============================================================================

@mcp.tool()
def logarithm(x: float, base: Optional[float] = None) -> float:
    """Calculate logarithm of x

    Args:
        x: Input value (must be positive)
        base: Logarithm base (default: natural logarithm)

    Returns:
        Logarithm of x with given base

    Raises:
        ValueError: If x is non-positive
    """
    if x <= 0:
        raise ValueError("Logarithm is only defined for positive numbers")
    
    if base is None:
        return math.log(x)  # 自然对数
    elif base <= 0 or base == 1:
        raise ValueError("Logarithm base must be positive and not equal to 1")
    return math.log(x, base)

@mcp.tool()
def exponent(x: float) -> float:
    """Calculate e raised to the power of x

    Args:
        x: Exponent

    Returns:
        e^x
    """
    return math.exp(x)

# ============================================================================
# 异步计算工具
# ============================================================================

@mcp.tool()
async def async_factorial(n: int) -> int:
    """Calculate factorial asynchronously

    Args:
        n: Positive integer

    Returns:
        Factorial of n

    Raises:
        ValueError: If n is negative
    """
    if n < 0:
        raise ValueError("Factorial is not defined for negative numbers")
    if n == 0:
        return 1

    # 模拟耗时计算
    await asyncio.sleep(0.1)
    result = 1
    for i in range(1, n + 1):
        result *= i
        # 允许事件循环处理其他任务
        await asyncio.sleep(0)
    return result

@mcp.tool()
async def async_prime_check(n: int) -> Dict[str, Union[bool, int, List[int]]]:
    """Check if a number is prime asynchronously

    Args:
        n: Number to check

    Returns:
        Dictionary with prime status and factors if not prime
    """
    if n <= 1:
        return {"is_prime": False, "number": n, "factors": []}
    if n == 2:
        return {"is_prime": True, "number": n}
    if n % 2 == 0:
        return {"is_prime": False, "number": n, "factors": [2, n//2]}

    # 异步检查因数
    await asyncio.sleep(0.1)
    factors = []
    for i in range(3, int(math.sqrt(n)) + 1, 2):
        if n % i == 0:
            factors.append(i)
            factors.append(n//i)
            break
        # 允许事件循环处理其他任务
        await asyncio.sleep(0)

    return {
        "is_prime": len(factors) == 0,
        "number": n,
        "factors": factors if factors else None
    }

# ============================================================================
# 统计函数
# ============================================================================

@mcp.tool()
def calculate_mean(numbers: List[float]) -> float:
    """Calculate mean of a list of numbers

    Args:
        numbers: List of numbers

    Returns:
        Mean value

    Raises:
        ValueError: If list is empty
    """
    if not numbers:
        raise ValueError("Cannot calculate mean of empty list")
    return sum(numbers) / len(numbers)

@mcp.tool()
def calculate_std_dev(numbers: List[float]) -> float:
    """Calculate standard deviation of a list of numbers

    Args:
        numbers: List of numbers

    Returns:
        Standard deviation

    Raises:
        ValueError: If list has fewer than 2 elements
    """
    if len(numbers) < 2:
        raise ValueError("Need at least 2 numbers to calculate standard deviation")
    mean = calculate_mean(numbers)
    variance = sum((x - mean) ** 2 for x in numbers) / (len(numbers) - 1)
    return math.sqrt(variance)

# ============================================================================
# 缓存工具示例
# ============================================================================

@mcp.tool()
@timed_cache(300)  # 缓存5分钟
def cached_math_constant(name: str) -> float:
    """Get mathematical constant with caching

    Args:
        name: Constant name (pi, e, phi, sqrt2)

    Returns:
        Constant value

    Raises:
        ValueError: If constant name is unknown
    """
    constants = {
        "pi": math.pi,
        "e": math.e,
        "phi": (1 + math.sqrt(5)) / 2,  # 黄金比例
        "sqrt2": math.sqrt(2)
    }
    
    if name not in constants:
        raise ValueError(f"Unknown constant: {name}. Available: {', '.join(constants.keys())}")
    return constants[name]

# ============================================================================
# 启动服务
# ============================================================================

if __name__ == "__main__":
    # 使用 stdio 传输方式启动服务
    mcp.run(transport="stdio")